
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "main.h"

#include "parse.h"
#include "nes.h"
#include "uniqnode.h"
#include "uniqstring.h"
#include "color.h"
#include "options.h"
#include "misc.h"
#include "graphml.h"

static int parsedformat = 0;
static int token = 0;
static int token1 = 0;
static const char *parser_error_string;
static int dotdebug = 0;

static int parsegccerrorfile (void);
static void parsederror (void);
static void parsedempty (void);
static int parse2 (struct usubg *subg);
static int is_a_node (struct usubg *subg);
static int is_a_edge (struct usubg *subg);
static int is_a_settings (struct usubg *subg);
static int is_a_subgraph (struct usubg *subg);
static int parsedot (char *fname, FILE * fstream);
static int parsedot2 (struct usubg *subg);
static int is_a_dotgraphdefault (struct usubg *subg);
static int is_a_dotnodedefault (struct usubg *subg);
static int is_a_dotedgedefault (struct usubg *subg);
static int is_a_dotoverlap (struct usubg *subg);
static int is_a_dotcolor (struct usubg *subg);
static int is_a_dotfillcolor (struct usubg *subg);
static int is_a_dotlabel (struct usubg *subg);
static int is_a_dotlabeljust (struct usubg *subg);
static int is_a_dotstyle (struct usubg *subg);
static int is_a_dotpenwidth (struct usubg *subg);
static int is_a_dotsubgraph (struct usubg *subg);
static int is_a_dotne (struct usubg *subg);

char *
parsererrorstring (void)
{
  return ((char *) parser_error_string);
}

int
parse (char *fname, FILE * fstream)
{
  int ret = 0;

  /* default to tys format */
  parsedformat = 0;
  yyin = fstream;
  yy_flex_debug = 0;
  tyslexinit (yyin);
  if (0)
    {
      while ((token = yylex ()) != 0)
	{
	  printf ("%d yytext=%s \n", token, yytext);
	}
    }
  parser_error_string = NULL;
  /* header */
  token = yylex ();
  if (token == '}')
    {
      parser_error_string =
	"expected 'void', 'strict', 'graph' or 'digraph' keyword";
      if (option_gccbug)
	{
	  prepare4newdata ();
	  ret = parsegccerrorfile ();
	  tyslexreset ();
	  return (ret);
	}
      else
	{
	  prepare4newdata ();
	  parsederror ();
	  tyslexreset ();
	  return (0);
	}
    }
  /* check for xml */
  if (token == '<')
    {
      token = yylex ();
      if (token == '?')
	{
	  token = yylex ();
	  if (token == TYS_XML)
	    {
	      parsedformat = 2;
	      tyslexreset ();
	      rewind (fstream);
	      ret = parsegraphml (fname, fstream);
	      return (ret);
	    }
	  else
	    {
	      parser_error_string = "expected xml data file, missing 'xml'";
	      prepare4newdata ();
	      parsederror ();
	      tyslexreset ();
	      return (0);
	    }
	}
      else
	{
	  parser_error_string = "expected xml data file, missing a '?'";
	  prepare4newdata ();
	  parsederror ();
	  tyslexreset ();
	  return (0);
	}
    }
  if (token == TYS_STRICT)
    {
      tyslexreset ();
      /* 'strict' is a graphviz keyword */
      ret = parsedot (fname, fstream);
      if (ret)
	{
	  prepare4newdata ();
	  parsederror ();
	  dotlexreset ();
	}
      else if (nl == NULL)
	{
	  prepare4newdata ();
	  parsedempty ();
	  dotlexreset ();
	}
      else
	{			/* oke */
	}
      return (0);
    }
  else if (token == TYS_GRAPH)
    {
      tyslexreset ();
      /* 'graph' at start is a graphviz graph */
      ret = parsedot (fname, fstream);
      if (ret)
	{
	  prepare4newdata ();
	  parsederror ();
	  dotlexreset ();
	}
      else if (nl == NULL)
	{
	  prepare4newdata ();
	  parsedempty ();
	  dotlexreset ();
	}
      else
	{			/* oke */
	}
      return (0);
    }
  else if (token == TYS_DIGRAPH)
    {
      tyslexreset ();
      /* 'digraph' at start is a graphviz graph */
      ret = parsedot (fname, fstream);
      if (ret)
	{
	  prepare4newdata ();
	  parsederror ();
	  dotlexreset ();
	}
      else if (nl == NULL)
	{
	  prepare4newdata ();
	  parsedempty ();
	  dotlexreset ();
	}
      else
	{			/* oke */
	}

      return (0);
    }
  else if (token != TYS_VOID)
    {
      parser_error_string = "expected 'void' keyword";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }
  else
    {				/* oke */
    }

  token = yylex ();
  if (token != TYS_GRAPH)
    {
      parser_error_string = "expected 'graph' keyword";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }

  token = yylex ();
  if (token != '(')
    {
      parser_error_string = "expected '(' at 'void graph(void)";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }

  token = yylex ();
  if (token != TYS_VOID)
    {
      parser_error_string = "expected 'void' keyword";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }

  token = yylex ();
  if (token != ')')
    {
      parser_error_string = "expected ')' at 'void graph(void)";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }

  token = yylex ();
  if (token != '{')
    {
      parser_error_string = "expected '{' after 'void graph(void)";
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }

  /* start parse at toplevel root graph */
  ret = parse2 (NULL);
  if (ret == 0)
    {
      if (parser_error_string == NULL)
	{
	  parser_error_string =
	    "expected node, edge, subgraph\nor settings in body of graph";
	}
      prepare4newdata ();
      parsederror ();
      tyslexreset ();
      return (0);
    }
  if (nl == NULL)
    {
      prepare4newdata ();
      parsedempty ();
    }
  else
    {				/* oke */
    }
  tyslexreset ();
  return (0);
}

/* gcc generated empty graphviz dot file with error */
static int
parsegccerrorfile (void)
{
  struct unode *un;
  un = unode_new ((char *) "gcc error");
  un->label = (char *) "empty graph";
  un->rootedon = NULL;
  /* edgelabel node to draw different */
  un->bitflags.edgelabel = 1;
  un->bitflags.parseerror = 1;
  uniqnode_add (un->name, un);
  unode_append_nodelist (un);
  usubg_add_node (un, NULL);
  return (0);
}

static void
parsederror (void)
{
  char buf[127];
  char *s = NULL;
  struct unode *un = NULL;
  int lineno = 0;
  if (parsedformat == 0)
    {
      lineno = tyslex_lineno ();
    }
  else if (parsedformat == 1)
    {
      lineno = dotlex_lineno ();
    }
  else if (parsedformat == 2)
    {
      lineno = graphml_lineno ();
    }
  else
    {
      lineno = 0;
    }
  memset (buf, 0, 127);
  snprintf (buf, (127 - 1), "parse error near line %d:\n%s", lineno,
	    parsererrorstring ());
  s = uniqstring (buf);
  un = unode_new ((char *) "parsederror");
  un->label = s;
  un->rootedon = NULL;
  /* edgelabel node to draw different */
  un->bitflags.edgelabel = 1;
  un->bitflags.parseerror = 1;
  uniqnode_add (un->name, un);
  unode_append_nodelist (un);
  usubg_add_node (un, NULL);
  return;
}

static void
parsedempty (void)
{
  struct unode *un;
  un = unode_new ((char *) "emptygraph");
  un->label = (char *) "empty graph";
  un->rootedon = NULL;
  /* edgelabel node to draw different */
  un->bitflags.edgelabel = 1;
  un->bitflags.parseerror = 1;
  uniqnode_add (un->name, un);
  unode_append_nodelist (un);
  usubg_add_node (un, NULL);
  return;
}

static int
parse2 (struct usubg *subg)
{

  for (;;)
    {
      token = yylex ();
      if (token == 0)
	{
	  return (0);
	}
      else if (token == '}')
	{
	  return (1);
	}
      else if (is_a_node (subg))
	{
	}
      else if (is_a_edge (subg))
	{
	}
      else if (is_a_settings (subg))
	{
	}
      else if (is_a_subgraph (subg))
	{

	}
      else
	{
	  return (0);
	}
    }
  return (1);
}

static int
is_a_node (struct usubg *subg)
{
  int color;
  struct unode *un;
  if (token != TYS_NODE)
    {
      return (0);
    }
  token = yylex ();
  if (token != '(')
    {
      parser_error_string = "expected '(' at node(\"nodename\")";
      return (0);
    }
  token = yylex ();
  if (token != TYS_STRING)
    {
      parser_error_string = "expected \"nodename\" at node(\"nodename\")";
      return (0);
    }
  token = yylex ();
  if (token != ')')
    {
      parser_error_string = "expected ')' at node(\"nodename\")";
      return (0);
    }
  un = uniqnode (laststring);
  if (un == (struct unode *) 0)
    {
      un = unode_new (laststring);
      un->label = un->name;
      un->rootedon = subg;
      /* node is defined by node statement */
      un->bitflags.defbynode = 1;
      un->fontsize = 0;
      uniqnode_add (un->name, un);
      unode_append_nodelist (un);
      usubg_add_node (un, subg);
    }
  else
    {
      if (un->rootedon == subg)
	{
	  /* oke */
	}
      else
	{
	  /* node is created in edge() but defined in node() in this other subgraph */
	  un->rootedon = subg;
	  usubg_add_node (un, subg);
	}
    }
  token = yylex ();
  if (token == ';')
    {
      return (1);
    }
  if (token != '{')
    {
      parser_error_string = "expected '{' at start of node attribute";
      return (0);
    }
  /* now {} with the node options */
  for (;;)
    {
      token = yylex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at node(\"nodename\") { }";
	}
      else if (token == '}')
	{
	  break;
	}
      else if (token == TYS_LABEL)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute label=\"string\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"string\" at node attribute label=\"string\";";
	      return (0);
	    }
	  if (option_nodenames)
	    {
	      un->label = un->name;
	    }
	  else
	    {
	      if (strlen (laststring) == 0)
		{
		  un->label = NULL;
		}
	      else
		{
		  un->label = laststring;
		}
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute label=\"string\";";
	      return (0);
	    }
	}
      else if (token == TYS_FONTNAME)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute fontname=\"string\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"fontname\" at node attribute fontname=\"string\";";
	      return (0);
	    }
	  if (strlen (laststring) == 0)
	    {
	      un->fontname = NULL;
	    }
	  else
	    {
	      un->fontname = laststring;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute fontname=\"string\";";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTSLANT)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute textslant=\"string\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"italic\" or \"oblique\" at node attribute textslant=\"string\";";
	      return (0);
	    }
	  if (strcmp (laststring, "italic") == 0)
	    {
	      un->bitflags.textitalic = 1;
	    }
	  else if (strcmp (laststring, "oblique") == 0)
	    {
	      un->bitflags.textoblique = 1;
	    }
	  else
	    {
	      /* slant is normal */
	      un->bitflags.textitalic = 0;
	      un->bitflags.textoblique = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute textslant=\"string\";";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTWEIGHT)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute textweight=\"string\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"bold\" at node attribute textweight=\"string\";";
	      return (0);
	    }
	  if (strcmp (laststring, "bold") == 0)
	    {
	      /* text is in weight bold */
	      un->bitflags.textbold = 1;
	    }
	  else
	    {
	      /* weight is normal */
	      un->bitflags.textbold = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected '=' at node attribute textweight=\"string\";";
	      return (0);
	    }
	}
      else if (token == TYS_FONTSIZE)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute fonsize=\"number\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"number\" at node attribute fonsize=\"number\";";
	      return (0);
	    }
	  un->fontsize = atoi (laststring);
	  if (un->fontsize < 1 || un->fontsize > 100)
	    {
	      /* use gtk default fontsize if out of range */
	      un->fontsize = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute fonsize=\"number\";";
	      return (0);
	    }
	}
      else if (token == TYS_COLOR)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute color=\"colorname\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"colorname\" at node attribute color=\"colorname\";";
	      return (0);
	    }
	  color = colorcode (laststring);
	  if (color == -1)
	    {
	      /* white background */
	      un->color = 0x00ffffff;
	    }
	  else
	    {
	      un->color = color;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute color=\"colorname\";";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTCOLOR)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute textcolor=\"colorname\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"colorname\" at node attribute textcolor=\"colorname\";";
	      return (0);
	    }
	  color = colorcode (laststring);
	  if (color == -1)
	    {
	      /* black text */
	      un->textcolor = 0x00;
	    }
	  else
	    {
	      un->textcolor = color;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute textcolor=\"colorname\";";
	      return (0);
	    }
	}
      else if (token == TYS_SHAPE)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at node attribute shape=\"nodeshape\";";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"nodeshape\" box, rbox, circle or ellips at node attribute shape=\"nodeshape\";";
	      return (0);
	    }
	  if (strcmp (laststring, "box") == 0)
	    {
	      un->shape = NSHAPE_BOX;
	    }
	  else if (strcmp (laststring, "rbox") == 0)
	    {
	      un->shape = NSHAPE_RBOX;
	    }
	  else if (strcmp (laststring, "circle") == 0)
	    {
	      un->shape = NSHAPE_CIRCLE;
	    }
	  else if (strcmp (laststring, "ellips") == 0)
	    {
	      un->shape = NSHAPE_ELLIPS;
	    }
	  else if (strcmp (laststring, "ellipse") == 0)
	    {
	      un->shape = NSHAPE_ELLIPS;
	    }
	  else
	    {
	      /* use precompiled default */
	      un->shape = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at node attribute shape=\"nodeshape\";";
	      return (0);
	    }
	}
      else
	{
	  return (0);
	}
    }

  return (1);
}

static int
is_a_edge (struct usubg *subg)
{
  char *elabel = NULL;
  int color = 0;
  int textcolor = 0;
  int textitalic = 0;
  int textoblique = 0;
  int textbold = 0;
  int fontsize = 0;
  char *fontname = NULL;
  int style = ESTYLE_NORMAL;
  char *fromname = NULL;
  char *toname = NULL;
  int selfedge = 0;
  struct unode *fn = NULL;
  struct unode *tn = NULL;
  struct unode *d1 = NULL;
  struct unode *d3 = NULL;
  char buf[32];
  char *es = NULL;
  struct uedge *ue = NULL;
  if (token != TYS_EDGE)
    {
      return (0);
    }
  token = yylex ();
  if (token != '(')
    {
      parser_error_string =
	"expected '(' at 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  token = yylex ();
  if (token != TYS_STRING)
    {
      parser_error_string =
	"expected \"from-name\" at 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  fromname = laststring;
  token = yylex ();
  if (token != ',')
    {
      parser_error_string =
	"expected ',' at 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  token = yylex ();
  if (token != TYS_STRING)
    {
      parser_error_string =
	"expected \"to-name\" at 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  toname = laststring;
  token = yylex ();
  if (token != ')')
    {
      parser_error_string =
	"expected ')' at 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  if (strcmp (fromname, toname) == 0)
    {
      selfedge = 1;
    }
  token = yylex ();
  if (token == ';')
    {
      elabel = NULL;
      color = 0;
      style = ESTYLE_NORMAL;
      /* check for self edges */
      if (option_selfedges == 0)
	{
	  if (selfedge)
	    {
	      fn = uniqnode (fromname);
	      if (fn == (struct unode *) 0)
		{
		  fn = unode_new (fromname);
		  fn->label = fn->name;
		  fn->rootedon = subg;
		  /* node is defined by edge statement */
		  fn->bitflags.defbyedge = 1;
		  uniqnode_add (fn->name, fn);
		  unode_append_nodelist (fn);
		  usubg_add_node (fn, subg);
		}
	      return (1);
	    }
	}
      /* */
      if (selfedge)
	{
	  fn = uniqnode (fromname);
	  if (fn == (struct unode *) 0)
	    {
	      fn = unode_new (fromname);
	      fn->label = fn->name;
	      fn->rootedon = subg;
	      /* node is defined by edge statement */
	      fn->bitflags.defbyedge = 1;
	      uniqnode_add (fn->name, fn);
	      unode_append_nodelist (fn);
	      usubg_add_node (fn, subg);
	    }
	  memset (buf, 0, 32);
	  snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
	  es = uniqstring (buf);
	  selfedgecount = selfedgecount + 1;
	  d1 = unode_new (es);
	  /* dummy node */
	  d1->label = NULL;
	  d1->rootedon = subg;
	  /* node is defined by edge statement */
	  d1->bitflags.defbyedge = 1;
	  /* edgelabel node to draw different */
	  d1->bitflags.edgelabel = 0;
	  /* node is part of selfedge */
	  d1->bitflags.selfedge = 1;
	  uniqnode_add (d1->name, d1);
	  unode_append_nodelist (d1);
	  usubg_add_node (d1, subg);
	  /* 1st edge */
	  ue = uedge_new (fn, d1);
	  ue->color = color;
	  ue->style = style;
	  ue->rootedon = subg;
	  ue->bitflags.selfedge = 1;
	  uedge_append_edgelist (ue);
	  usubg_add_edge (ue, subg);
	  /* next node */
	  memset (buf, 0, 32);
	  snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
	  es = uniqstring (buf);
	  selfedgecount = selfedgecount + 1;
	  d3 = unode_new (es);
	  /* dummy node */
	  d3->label = NULL;
	  d3->rootedon = subg;
	  /* node is defined by edge statement */
	  d3->bitflags.defbyedge = 1;
	  /* edgelabel node to draw different */
	  d3->bitflags.edgelabel = 0;
	  /* node is part of selfedge */
	  d3->bitflags.selfedge = 1;
	  uniqnode_add (d3->name, d3);
	  unode_append_nodelist (d3);
	  usubg_add_node (d3, subg);
	  /* 2nd edge */
	  ue = uedge_new (d1, d3);
	  ue->color = color;
	  ue->style = style;
	  ue->rootedon = subg;
	  if (elabel)
	    {
	      ue->label = elabel;
	      ue->textcolor = 0;	/* black text */
	      ue->fontsize = 0;	/* gtk default */
	      ue->fontname = NULL;	/* serif */
	      ue->bitflags.textbold = 0;
	      ue->bitflags.textitalic = 0;
	      ue->bitflags.textoblique = 0;
	    }
	  ue->bitflags.selfedge = 1;
	  uedge_append_edgelist (ue);
	  usubg_add_edge (ue, subg);
	  /* 4th edge */
	  ue = uedge_new (d3, fn);
	  ue->color = color;
	  ue->style = style;
	  ue->rootedon = subg;
	  ue->bitflags.selfedge = 1;
	  uedge_append_edgelist (ue);
	  usubg_add_edge (ue, subg);
	  return (1);
	}
      else
	{
	  /* regular edge */
	  fn = uniqnode (fromname);
	  if (fn == (struct unode *) 0)
	    {
	      fn = unode_new (fromname);
	      fn->label = fn->name;
	      fn->rootedon = subg;
	      /* node is defined by edge statement */
	      fn->bitflags.defbyedge = 1;
	      uniqnode_add (fn->name, fn);
	      unode_append_nodelist (fn);
	      usubg_add_node (fn, subg);
	    }
	  tn = uniqnode (toname);
	  if (tn == (struct unode *) 0)
	    {
	      tn = unode_new (toname);
	      tn->label = tn->name;
	      tn->rootedon = subg;
	      /* node is defined by edge statement */
	      tn->bitflags.defbyedge = 1;
	      uniqnode_add (tn->name, tn);
	      unode_append_nodelist (tn);
	      usubg_add_node (tn, subg);
	    }
	  ue = uedge_new (fn, tn);
	  /* black edge line */
	  ue->color = 0x00;
	  /* solid edge line */
	  ue->style = ESTYLE_NORMAL;
	  ue->rootedon = subg;
	  uedge_append_edgelist (ue);
	  usubg_add_edge (ue, subg);
	  return (1);
	}
    }
  if (token != '{')
    {
      parser_error_string =
	"expected '{' after 'edge(\"from-name\",\"to-name\")'";
      return (0);
    }
  elabel = NULL;
  color = 0;
  fontsize = 0;
  textcolor = 0;
  textitalic = 0;
  textoblique = 0;
  textbold = 0;
  fontname = NULL;
  style = ESTYLE_NORMAL;
  for (;;)
    {
      token = yylex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at edge attribute";
	  return (0);
	}
      else if (token == '}')
	{
	  break;
	}
      else if (token == TYS_COLOR)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'color=\"colorname\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"colorname\" at edge 'color=\"colorname\");' attribute";
	      return (0);
	    }
	  color = colorcode (laststring);
	  if (color == -1)
	    {
	      /* black edge line */
	      color = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'color=\"colorname\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTCOLOR)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'textcolor=\"colorname\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"colorname\" at edge 'textcolor=\"colorname\");' attribute";
	      return (0);
	    }
	  textcolor = colorcode (laststring);
	  if (color == -1)
	    {
	      /* black label text color */
	      textcolor = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'textcolor=\"colorname\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_LABEL)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'label=\"labelname\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"labelname\" at edge 'label=\"labelname\");' attribute";
	      return (0);
	    }
	  elabel = laststring;
	  if (strlen (laststring) == 0)
	    {
	      elabel = NULL;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'label=\"labelname\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_FONTNAME)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'fontname=\"fontname\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"fontname\" at edge 'fontname=\"fontname\");' attribute";
	      return (0);
	    }
	  if (strlen (laststring) == 0)
	    {
	      fontname = NULL;
	    }
	  else
	    {
	      fontname = laststring;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'fontname=\"fontname\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTSLANT)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'textslant=\"slant\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"slant\" at edge 'textslant=\"slant\");' attribute";
	      return (0);
	    }
	  if (strcmp (laststring, "italic") == 0)
	    {
	      textitalic = 1;
	    }
	  else if (strcmp (laststring, "oblique") == 0)
	    {
	      textoblique = 1;
	    }
	  else
	    {
	      /* slant is normal */
	      textitalic = 0;
	      textoblique = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'textslant=\"slant\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_TEXTWEIGHT)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'textweight=\"weight\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"slant\" at edge 'textweight=\"weight\");' attribute";
	      return (0);
	    }
	  if (strcmp (laststring, "bold") == 0)
	    {
	      /* bold text weight */
	      textbold = 1;
	    }
	  else
	    {
	      /* weight is normal */
	      textbold = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'textweight=\"weight\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_FONTSIZE)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'fontsize=\"number\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"number\" at edge 'fontsize=\"number\");' attribute";
	      return (0);
	    }
	  fontsize = atoi (laststring);
	  if (fontsize < 1 || fontsize > 100)
	    {
	      /* use gtk default, 0 is value not set */
	      fontsize = 0;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'fontsize=\"number\");' attribute";
	      return (0);
	    }
	}
      else if (token == TYS_STYLE)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string =
		"expected '=' at edge 'style=\"stylename\");' attribute";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"stylename\" at edge 'style=\"stylename\");' attribute";
	      return (0);
	    }
	  if (strcmp (laststring, "normal") == 0)
	    {
	      style = ESTYLE_NORMAL;
	    }
	  else if (strcmp (laststring, "solid") == 0)
	    {
	      style = ESTYLE_SOLID;
	    }
	  else if (strcmp (laststring, "dotted") == 0)
	    {
	      style = ESTYLE_DOTTED;
	    }
	  else if (strcmp (laststring, "dashed") == 0)
	    {
	      style = ESTYLE_DASHED;
	    }
	  else if (strcmp (laststring, "invis") == 0)
	    {
	      style = ESTYLE_INVIS;
	    }
	  else
	    {
	      style = ESTYLE_NORMAL;
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string =
		"expected ';' at edge 'style=\"stylename\");' attribute";
	      return (0);
	    }
	}
      else
	{
	  parser_error_string = "unknown edge attribute";
	  return (0);
	}
    }
  /* option to not include edgelabels in the graph */
  if (option_no_edgelabels)
    {
      elabel = NULL;
    }
  /* check for self edges */
  if (option_selfedges == 0)
    {
      if (selfedge)
	{
	  fn = uniqnode (fromname);
	  if (fn == (struct unode *) 0)
	    {
	      fn = unode_new (fromname);
	      fn->label = fn->name;
	      fn->rootedon = subg;
	      /* node is defined by edge statement */
	      fn->bitflags.defbyedge = 1;
	      uniqnode_add (fn->name, fn);
	      unode_append_nodelist (fn);
	      usubg_add_node (fn, subg);
	    }
	  return (1);
	}
    }
  /* */
  if (selfedge)
    {

      fn = uniqnode (fromname);
      if (fn == (struct unode *) 0)
	{
	  fn = unode_new (fromname);
	  fn->label = fn->name;
	  fn->rootedon = subg;
	  /* node is defined by edge statement */
	  fn->bitflags.defbyedge = 1;
	  uniqnode_add (fn->name, fn);
	  unode_append_nodelist (fn);
	  usubg_add_node (fn, subg);
	}
      memset (buf, 0, 32);
      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
      es = uniqstring (buf);
      selfedgecount = selfedgecount + 1;
      d1 = unode_new (es);
      /* dummy node */
      d1->label = NULL;
      d1->rootedon = subg;
      /* node is defined by edge statement */
      d1->bitflags.defbyedge = 1;
      /* edgelabel node to draw different */
      d1->bitflags.edgelabel = 0;
      /* node is part of selfedge */
      d1->bitflags.selfedge = 1;
      uniqnode_add (d1->name, d1);
      unode_append_nodelist (d1);
      usubg_add_node (d1, subg);
      /* 1st edge */
      ue = uedge_new (fn, d1);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      /* next node */
      memset (buf, 0, 32);
      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
      es = uniqstring (buf);
      selfedgecount = selfedgecount + 1;
      d3 = unode_new (es);
      /* dummy node */
      d3->label = NULL;
      d3->rootedon = subg;
      /* node is defined by edge statement */
      d3->bitflags.defbyedge = 1;
      /* edgelabel node to draw different */
      d3->bitflags.edgelabel = 0;
      /* node is part of selfedge */
      d3->bitflags.selfedge = 1;
      uniqnode_add (d3->name, d3);
      unode_append_nodelist (d3);
      usubg_add_node (d3, subg);
      /* 2nd edge */
      ue = uedge_new (d1, d3);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      if (elabel)
	{
	  ue->label = elabel;
	  ue->textcolor = textcolor;
	  ue->fontsize = fontsize;
	  ue->fontname = fontname;
	  if (textbold)
	    {
	      ue->bitflags.textbold = 1;
	    }
	  if (textitalic)
	    {
	      ue->bitflags.textitalic = 1;
	    }
	  if (textoblique)
	    {
	      ue->bitflags.textoblique = 1;
	    }
	}
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      /* 4th edge */
      ue = uedge_new (d3, fn);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      return (1);
    }
  /* regular edge with or without edge label */
  fn = uniqnode (fromname);
  if (fn == (struct unode *) 0)
    {
      fn = unode_new (fromname);
      fn->label = fn->name;
      fn->rootedon = subg;
      /* node is defined by edge statement */
      fn->bitflags.defbyedge = 1;
      uniqnode_add (fn->name, fn);
      unode_append_nodelist (fn);
      usubg_add_node (fn, subg);
    }
  tn = uniqnode (toname);
  if (tn == (struct unode *) 0)
    {
      tn = unode_new (toname);
      tn->label = tn->name;
      tn->rootedon = subg;
      /* node is defined by edge statement */
      tn->bitflags.defbyedge = 1;
      uniqnode_add (tn->name, tn);
      unode_append_nodelist (tn);
      usubg_add_node (tn, subg);
    }
  if (elabel)
    {
      if (strlen (elabel) == 0)
	{
	  elabel = NULL;
	}
    }
  if (elabel)
    {
      /* 2nd edge */
      ue = uedge_new (fn, tn);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      if (elabel)
	{
	  ue->label = elabel;
	  ue->textcolor = textcolor;	/* black text */
	  ue->fontsize = fontsize;	/* gtk default */
	  ue->fontname = fontname;	/* default font */
	  if (textbold)
	    {
	      ue->bitflags.textbold = 1;
	    }
	  if (textitalic)
	    {
	      ue->bitflags.textitalic = 1;
	    }
	  if (textoblique)
	    {
	      ue->bitflags.textoblique = 1;
	    }
	}
      ue->bitflags.selfedge = 0;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
    }
  else
    {
      ue = uedge_new (fn, tn);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
    }
  return (1);
}

static int
is_a_settings (struct usubg *subg)
{
  int color = 0;
  if (token != TYS_SETTINGS)
    {
      return (0);
    }
  token = yylex ();
  if (token != '(')
    {
      parser_error_string = "expected '(' at 'settings(void)'";
      return (0);
    }
  token = yylex ();
  if (token != TYS_VOID)
    {
      parser_error_string = "expected 'void' at 'settings(void)'";
      return (0);
    }
  token = yylex ();
  if (token != ')')
    {
      parser_error_string = "expected ')' at 'settings(void)'";
      return (0);
    }
  token = yylex ();
  if (token == ';')
    {
      return (1);
    }
  if (token != '{')
    {
      parser_error_string = "expected '{' at 'settings(void)'";
      return (0);
    }
  /* default white background color */
  color = 0x00ffffff;
  /* now {} with the settings options */
  for (;;)
    {
      token = yylex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at 'settings(void){}'";
	  return (0);
	}
      else if (token == '}')
	{
	  break;
	}
      else if (token == TYS_COLOR)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string = "expected '=' at 'color=\"colorname\";'";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"color name\" at 'color=\"colorname\";'";
	      return (0);
	    }
	  color = colorcode (laststring);
	  if (color == -1)
	    {
	      /* unknown color choose default white */
	      if (subg == NULL)
		{
		  /* white background color of the root graph */
		  bgcr = 0xff;
		  bgcg = 0xff;
		  bgcb = 0xff;
		}
	      else
		{
		  /* white background color of the summary node */
		  subg->summaryn->color = 0x00ffffff;
		}
	    }
	  else
	    {
	      /* specified background color of the root graph */
	      if (subg == NULL)
		{
		  bgcr = (color & 0x00ff0000) >> 16;
		  bgcg = (color & 0x0000ff00) >> 8;
		  bgcb = (color & 0x000000ff);
		}
	      else
		{
		  /* background color of the summary node */
		  subg->summaryn->color = color;
		}
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string = "expected ';' at 'color=\"colorname\";'";
	      return (0);
	    }
	}
      else if (token == TYS_LABEL)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string = "expected '=' at 'label=\"string\";'";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"string\" at 'label=\"string\";'";
	      return (0);
	    }
	  if (subg == NULL)
	    {
	      /* root graph does not have a label name */
	    }
	  else
	    {
	      if (option_nodenames)
		{
		  subg->summaryn->label = subg->summaryn->name;
		}
	      else
		{
		  if (strlen (laststring) == 0)
		    {
		      subg->summaryn->label = subg->summaryn->name;
		    }
		  else
		    {
		      subg->summaryn->label = laststring;
		    }
		}
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string = "expected ';' at 'label=\"string\";'";
	      return (0);
	    }
	}
      else if (token == TYS_FOLDED)
	{
	  token = yylex ();
	  if (token != '=')
	    {
	      parser_error_string = "expected '=' at 'folded=\"yes/no\";'";
	      return (0);
	    }
	  token = yylex ();
	  if (token != TYS_STRING)
	    {
	      parser_error_string =
		"expected \"yes\" or \"no\" at 'folded=\"yes/no\";'";
	      return (0);
	    }
	  if (subg == NULL)
	    {
	      /* root graph cannot be folded */
	      /* folding setting for all subgraphs at once */
	      if (strcmp (laststring, "yes") == 0)
		{
		  settings_init_folded = 2;
		}
	      else if (strcmp (laststring, "no") == 0)
		{
		  settings_init_folded = 3;
		}
	      else
		{
		  settings_init_folded = 0;
		}
	    }
	  else
	    {
	      if (strcmp (laststring, "yes") == 0)
		{
		  subg->bitflags.folded = 1;
		}
	      else if (strcmp (laststring, "no") == 0)
		{
		  subg->bitflags.folded = 0;
		}
	      else
		{
		  parser_error_string =
		    "expected \"yes\" or \"no\" at 'folded=\"yes/no\";'";
		  return (0);
		}
	      /* commandline option overrides the graph data option */
	      if (option_init_unfolded)
		{
		  subg->bitflags.folded = 0;
		}
	      if (option_init_folded)
		{
		  subg->bitflags.folded = 1;
		}
	    }
	  token = yylex ();
	  if (token != ';')
	    {
	      parser_error_string = "expected ';' at 'folded=\"yes/no\";'";
	      return (0);
	    }
	}
      else
	{
	  return (0);
	}
    }
  return (1);
}

static int
is_a_subgraph (struct usubg *subg)
{
  struct usubg *newsg = NULL;
  char *sgname = NULL;
  int ret = 0;
  if (token != TYS_SUBGRAPH)
    {
      return (0);
    }
  token = yylex ();
  if (token != '(')
    {
      parser_error_string = "expected '(' at 'subgraph(\"subgraphname\")'";
      return (0);
    }
  token = yylex ();
  if (token != TYS_STRING)
    {
      parser_error_string =
	"expected \"string\" at 'subgraph(\"subgraphname\")'";
      return (0);
    }
  sgname = laststring;
  token = yylex ();
  if (token != ')')
    {
      parser_error_string = "expected ')' at 'subgraph(\"subgraphname\")'";
      return (0);
    }
  token = yylex ();
  if (token == ';')
    {
      /* empty subgraph */
      if (sgname)
	{
	  newsg = usubg_new (sgname, subg);
	  if (option_init_unfolded)
	    {
	      newsg->bitflags.folded = 0;
	    }
	  if (option_init_folded)
	    {
	      newsg->bitflags.folded = 1;
	    }
	  /* empty subgraph is folded */
	  newsg->bitflags.folded = 1;
	  usubg_add (newsg, subg);
	  newsg->summaryn->rootedon = subg;
	  newsg->summaryn->color = 0x00ffffff;
	  /* this is a subgraph summary node */
	  newsg->summaryn->bitflags.sumnode = 1;
	}
      return (1);
    }
  if (token != '{')
    {
      parser_error_string = "expected '{' at 'subgraph(\"subgraphname\")'";
      return (0);
    }
  if (sgname)
    {
      newsg = usubg_new (sgname, subg);
      if (option_init_unfolded)
	{
	  newsg->bitflags.folded = 0;
	}
      if (option_init_folded)
	{
	  newsg->bitflags.folded = 1;
	}
      usubg_add (newsg, subg);
      newsg->summaryn->rootedon = subg;
      newsg->summaryn->color = 0x00ffffff;
      /* this is a subgraph summary node */
      newsg->summaryn->bitflags.sumnode = 1;
    }
  else
    {
      parser_error_string = "expected new subgraph";
      return (0);
    }

  ret = parse2 (newsg);

  if (newsg->sg == NULL && newsg->nl == NULL)
    {
      /* empty subgraph is folded */
      newsg->bitflags.folded = 1;
    }

  return (ret);
}

static int
parsedot (char *fname, FILE * fstream)
{
  parsedformat = 1;
  dotin = fstream;
  dot_flex_debug = dotdebug;
  dotlexinit (dotin);
  rewind (fstream);
  parser_error_string = NULL;
  token = 0;
  token1 = 0;
  token = dotlex ();

  if (fname)
    {
    }

  if (token == DOT_STRICT)
    {
      token = dotlex ();
    }
  if (token == DOT_GRAPH)
    {
    }
  else if (token == DOT_DIGRAPH)
    {
    }
  else
    {
      parser_error_string = "expected 'graph' or 'digraph' at start";
      return (1);
    }
  token = dotlex ();
  if (token == DOT_STRING)
    {
      token = dotlex ();
    }
  else if (token == DOT_ID)
    {
      token = dotlex ();
    }
  else
    {				/* empty graphname */
    }
  if (token != '{')
    {
      parser_error_string = "expected '{' after 'graphname'";
      return (1);
    }
  if (parsedot2 (NULL) == 0)
    {
      if (parser_error_string == NULL)
	{
	  parser_error_string =
	    "parse error in body of graph or missing a closing '}'";
	}
      return (1);
    }
  dotlexreset ();
  return (0);
}


static int
parsedot2 (struct usubg *subg)
{

  for (;;)
    {
      if (token1)
	{
	  token = token1;
	  token1 = 0;
	}
      else
	{
	  token = dotlex ();
	}
      if (token == 0)
	{
	  if (subg)
	    {
	      return (0);
	    }
	  else
	    {
	      if (option_gccbug)
		{
		  /* at rootgraph possibly gcc file missing close brace. dot does not dtect this */
		  /* gcc produces dot files missing closing brace, to compensate return(1) instead of return(0) */
		  return (1);
		}
	      else
		{
		  return (0);
		}
	    }
	}
      else if (token == '}')
	{
	  return (1);
	}
      else if (is_a_dotgraphdefault (subg))
	{
	}
      else if (is_a_dotnodedefault (subg))
	{
	}
      else if (is_a_dotedgedefault (subg))
	{
	}
      else if (is_a_dotoverlap (subg))
	{
	}
      else if (is_a_dotcolor (subg))
	{
	}
      else if (is_a_dotfillcolor (subg))
	{
	}
      else if (is_a_dotlabel (subg))
	{
	}
      else if (is_a_dotlabeljust (subg))
	{
	}
      else if (is_a_dotstyle (subg))
	{
	}
      else if (is_a_dotpenwidth (subg))
	{
	}
      else if (is_a_dotsubgraph (subg))
	{
	}
      else if (is_a_dotne (subg))
	{
	}
      else
	{
	  return (0);
	}
    }

  return (1);
}

static int
is_a_dotgraphdefault (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_GRAPH)
    {
      return (0);
    }
  /* [ */
  token = dotlex ();
  if (token != '[')
    {
      token1 = token;
      parser_error_string = "expected '[' at graph default";
      return (0);
    }
  for (;;)
    {
      token = dotlex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at graph default";
	  return (0);
	}
      else if (token == ']')
	{
	  break;
	}
      else
	{
	  /* skip */
	}
    }
  return (1);
}

static int
is_a_dotnodedefault (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_NODEDEFAULT)
    {
      return (0);
    }
  /* [ */
  token = dotlex ();
  if (token != '[')
    {
      token1 = token;
      parser_error_string = "expected '[' at node default";
      return (0);
    }
  for (;;)
    {
      token = dotlex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at node default";
	  return (0);
	}
      else if (token == ']')
	{
	  break;
	}
      else
	{
	  /* skip */
	}
    }
  return (1);
}

static int
is_a_dotedgedefault (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_EDGEDEFAULT)
    {
      return (0);
    }
  /* [ */
  token = dotlex ();
  if (token != '[')
    {
      token1 = token;
      parser_error_string = "expected '[' at edge default";
      return (0);
    }
  for (;;)
    {
      token = dotlex ();
      if (token == 0)
	{
	  parser_error_string = "end of file at edge default";
	  return (0);
	}
      else if (token == ']')
	{
	  break;
	}
      else
	{
	  /* skip */
	}
    }
  return (1);
}

static int
is_a_dotoverlap (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_OVERLAP)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* true/false "true" "false" */
  token = dotlex ();
  return (1);
}

static int
is_a_dotcolor (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_COLOR)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* colorname "colorname" */
  token = dotlex ();
  return (1);
}


static int
is_a_dotfillcolor (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_FILLCOLOR)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* colorname "colorname" */
  token = dotlex ();
  return (1);
}


static int
is_a_dotlabel (struct usubg *subg)
{
  if (token != DOT_LABEL)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* lebelname "labelname" */
  token = dotlex ();
  if (subg)
    {
      if (token == DOT_STRING)
	{
	  subg->label = laststring;
	}
      else if (token == DOT_ID)
	{
	  subg->label = lastid;
	}
      else
	{
	  subg->label = subg->name;
	}
    }
  return (1);
}


static int
is_a_dotlabeljust (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_LABELJUST)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* lebeljustname "labeljustname" */
  token = dotlex ();
  return (1);
}


static int
is_a_dotstyle (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_STYLE)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* style "style" */
  token = dotlex ();
  return (1);
}


static int
is_a_dotpenwidth (struct usubg *subg)
{
  if (subg)
    {
    }

  if (token != DOT_PENWIDTH)
    {
      return (0);
    }
  /* = */
  token = dotlex ();
  /* number "number" */
  token = dotlex ();
  return (1);
}

static int
is_a_dotsubgraph (struct usubg *subg)
{
  struct usubg *nsg = NULL;
  char *sgname = NULL;
  if (token != DOT_SUBGRAPH)
    {
      return (0);
    }
  /* subgraphname "subgraphname" */
  token = dotlex ();
  if (token == DOT_ID)
    {
      sgname = lastid;
    }
  else if (token == DOT_STRING)
    {
      sgname = laststring;
    }
  else if (token == DOT_NUM)
    {
      sgname = lastnum;
    }
  else
    {
      parser_error_string =
	"expected id, \"string\" or number as subgraph name";
      return (0);
    }
  nsg = usubg_new (sgname, subg);
  if (option_init_unfolded)
    {
      nsg->bitflags.folded = 0;
    }
  if (option_init_folded)
    {
      nsg->bitflags.folded = 1;
    }
  /* graphviz dot files always folded initially */
  nsg->bitflags.folded = 1;
  usubg_add (nsg, subg);
  nsg->summaryn->rootedon = subg;
  nsg->summaryn->color = 0x00ffffff;
  /* this is a subgraph summary node */
  nsg->summaryn->bitflags.sumnode = 1;
  /* brace open */
  token = dotlex ();
  if (token != '{')
    {
      parser_error_string = "expected '{' at start of subgraph";
      return (0);
    }
  return (parsedot2 (nsg));
}

/* node or edge */
static int
is_a_dotne (struct usubg *subg)
{
  int selfedge = 0;
  char *fname = NULL;
  char *flabel = NULL;
  char *tname = NULL;
  char *elabel = NULL;
  int color = 0;
  int style = ESTYLE_NORMAL;
  struct unode *d1 = NULL;
  struct unode *d3 = NULL;
  struct unode *un = NULL;
  struct unode *fnode = NULL;
  struct unode *tnode = NULL;
  char buf[32];
  char *es = NULL;
  struct uedge *ue = NULL;
  if (token == DOT_STRING || token == DOT_ID || token == DOT_NUM)
    {
      selfedge = 0;
      flabel = NULL;
      elabel = NULL;
      color = 0x00ffffff;
      if (token == DOT_STRING)
	{
	  fname = laststring;
	}
      else if (token == DOT_NUM)
	{
	  fname = lastnum;
	}
      else
	{
	  fname = lastid;
	}
      /* : or --/-> or [ or id */
      token = dotlex ();
      if (token == ':')
	{
	  /* id */
	  token = dotlex ();
	  /* --/-> or [ or id */
	  token = dotlex ();
	}
      if (token == DOT_UEDGE || token == DOT_DEDGE)
	{
	  /* edge */
	  /* id "string" */
	  token = dotlex ();
	  if (token == DOT_STRING)
	    {
	      tname = laststring;
	    }
	  else if (token == DOT_NUM)
	    {
	      tname = lastnum;
	    }
	  else
	    {
	      tname = lastid;
	    }
	  /* : or [ or id */
	  token = dotlex ();
	  if (token == ':')
	    {
	      /* id */
	      token = dotlex ();
	      /* [ or id */
	      token = dotlex ();
	    }
	  /* check for self edge a->a */
	  if (strcmp (fname, tname) == 0)
	    {
	      selfedge = 1;
	    }
	  color = 0;
	  style = ESTYLE_NORMAL;
	  elabel = NULL;
	  /* optional attributes of edge */
	  if (token == '[')
	    {
	      for (;;)
		{
		  token = dotlex ();
		  if (token == 0)
		    {
		      return (0);
		    }
		  else if (token == ']')
		    {
		      break;
		    }
		  else if (token == DOT_STYLE)
		    {
		      /* = */
		      token = dotlex ();
		      /* style "style" */
		      token = dotlex ();
		      if (token == DOT_STRING)
			{
			  if (strcmp (laststring, "normal") == 0)
			    {
			      style = ESTYLE_NORMAL;
			    }
			  else if (strcmp (laststring, "solid") == 0)
			    {
			      style = ESTYLE_SOLID;
			    }
			  else if (strcmp (laststring, "dotted") == 0)
			    {
			      style = ESTYLE_DOTTED;
			    }
			  else if (strcmp (laststring, "dashed") == 0)
			    {
			      style = ESTYLE_DASHED;
			    }
			  else if (strcmp (laststring, "invis") == 0)
			    {
			      style = ESTYLE_INVIS;
			    }
			  else
			    {
			      style = ESTYLE_NORMAL;
			    }
			}
		      else if (token == DOT_NORMAL)
			{
			  style = ESTYLE_NORMAL;
			}
		      else if (token == DOT_SOLID)
			{
			  style = ESTYLE_SOLID;
			}
		      else if (token == DOT_DOTTED)
			{
			  style = ESTYLE_DOTTED;
			}
		      else if (token == DOT_DASHED)
			{
			  style = ESTYLE_DASHED;
			}
		      else if (token == DOT_INVIS)
			{
			  style = ESTYLE_INVIS;
			}
		      else
			{
			  style = ESTYLE_NORMAL;
			}
		    }
		  else if (token == DOT_COLOR)
		    {
		      /* = */
		      token = dotlex ();
		      /* color "color" */
		      token = dotlex ();
		      if (token == DOT_BLACK)
			{
			  color = colorcode ("black");
			}
		      else if (token == DOT_GREEN)
			{
			  color = colorcode ("green");
			}
		      else if (token == DOT_BLUE)
			{
			  color = colorcode ("blue");
			}
		      else if (token == DOT_RED)
			{
			  color = colorcode ("red");
			}
		      else
			{
			  /* black edge line */
			  color = 0;
			}
		    }
		  else if (token == DOT_WEIGHT)
		    {
		      /* = */
		      token = dotlex ();
		      /* number */
		      token = dotlex ();
		    }
		  else if (token == DOT_CONSTRAINT)
		    {
		      /* = */
		      token = dotlex ();
		      /* true/false "true" "false" */
		      token = dotlex ();
		    }
		  else if (token == DOT_LABEL)
		    {
		      /* = */
		      token = dotlex ();
		      /* "string" */
		      token = dotlex ();
		      elabel = laststring;
		      if (strlen (laststring) == 0)
			{
			  elabel = NULL;
			}
		    }
		  else
		    {
		      /* = */
		      token = dotlex ();
		      /* ? */
		      token = dotlex ();
		    }
		}
	    }
	  else
	    {
	      token1 = token;
	    }
	  /* check for self edges */
	  if (option_selfedges == 0)
	    {
	      if (selfedge)
		{
		  /* this is self edge not to add */
		  fnode = uniqnode (fname);
		  if (fnode == (struct unode *) 0)
		    {
		      fnode = unode_new (fname);
		      fnode->label = fname;
		      fnode->rootedon = subg;
		      /* node is defined by edge statement */
		      fnode->bitflags.defbyedge = 1;
		      uniqnode_add (fnode->name, fnode);
		      unode_append_nodelist (fnode);
		      usubg_add_node (fnode, subg);
		    }
		  /* end of edge */
		  return (1);
		}
	    }
	  /* */
	  if (selfedge)
	    {
	      /* option to not include edgelabels in the graph */
	      if (option_no_edgelabels)
		{
		  elabel = NULL;
		}
	      /* selfedge with or without edge label */
	      fnode = uniqnode (fname);
	      if (fnode == (struct unode *) 0)
		{
		  fnode = unode_new (fname);
		  fnode->label = fname;
		  fnode->rootedon = subg;
		  /* node is defined by edge statement */
		  fnode->bitflags.defbyedge = 1;
		  uniqnode_add (fnode->name, fnode);
		  unode_append_nodelist (fnode);
		  usubg_add_node (fnode, subg);
		}
	      memset (buf, 0, 32);
	      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
	      es = uniqstring (buf);
	      selfedgecount = selfedgecount + 1;
	      d1 = unode_new (es);
	      /* dummy node */
	      d1->label = NULL;
	      d1->rootedon = subg;
	      /* node is defined by edge statement */
	      d1->bitflags.defbyedge = 1;
	      /* edgelabel node to draw different */
	      d1->bitflags.edgelabel = 0;
	      /* node is part of selfedge */
	      d1->bitflags.selfedge = 1;
	      uniqnode_add (d1->name, d1);
	      unode_append_nodelist (d1);
	      usubg_add_node (d1, subg);
	      /* 1st edge */
	      ue = uedge_new (fnode, d1);
	      ue->color = color;
	      ue->style = style;
	      ue->rootedon = subg;
	      ue->bitflags.selfedge = 1;
	      uedge_append_edgelist (ue);
	      usubg_add_edge (ue, subg);
	      /* next node */
	      memset (buf, 0, 32);
	      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
	      es = uniqstring (buf);
	      selfedgecount = selfedgecount + 1;
	      d3 = unode_new (es);
	      /* dummy node */
	      d3->label = NULL;
	      d3->rootedon = subg;
	      /* node is defined by edge statement */
	      d3->bitflags.defbyedge = 1;
	      /* edgelabel node to draw different */
	      d3->bitflags.edgelabel = 0;
	      /* node is part of selfedge */
	      d3->bitflags.selfedge = 1;
	      uniqnode_add (d3->name, d3);
	      unode_append_nodelist (d3);
	      usubg_add_node (d3, subg);
	      /* 2nd edge */
	      ue = uedge_new (d1, d3);
	      ue->color = color;
	      ue->style = style;
	      ue->rootedon = subg;
	      if (elabel)
		{
		  ue->label = elabel;
		  ue->textcolor = 0;	/* black text */
		  ue->fontsize = 0;	/* gtk default */
		  ue->fontname = NULL;	/* default font */
		}
	      ue->bitflags.selfedge = 1;
	      uedge_append_edgelist (ue);
	      usubg_add_edge (ue, subg);
	      /* 4th edge */
	      ue = uedge_new (d3, fnode);
	      ue->color = color;
	      ue->style = style;
	      ue->rootedon = subg;
	      ue->bitflags.selfedge = 1;
	      uedge_append_edgelist (ue);
	      usubg_add_edge (ue, subg);
	    }
	  else
	    {
	      /* regular edge or self edge to add */
	      fnode = uniqnode (fname);
	      if (fnode == (struct unode *) 0)
		{
		  fnode = unode_new (fname);
		  fnode->label = fname;
		  fnode->rootedon = subg;
		  /* node is defined by edge statement */
		  fnode->bitflags.defbyedge = 1;
		  uniqnode_add (fnode->name, fnode);
		  unode_append_nodelist (fnode);
		  usubg_add_node (fnode, subg);
		}
	      tnode = uniqnode (tname);
	      if (tnode == (struct unode *) 0)
		{
		  tnode = unode_new (tname);
		  tnode->label = tname;
		  tnode->rootedon = subg;
		  /* node is defined by edge statement */
		  tnode->bitflags.defbyedge = 1;
		  uniqnode_add (tnode->name, tnode);
		  unode_append_nodelist (tnode);
		  usubg_add_node (tnode, subg);
		}
	      /* option to not include edgelabels in the graph */
	      if (option_no_edgelabels)
		{
		  elabel = NULL;
		}
	      /* */
	      if (elabel)
		{
		  /* regular edge with edge label */
		  ue = uedge_new (fnode, tnode);
		  ue->color = color;
		  ue->style = style;
		  ue->rootedon = subg;
		  ue->label = elabel;
		  ue->textcolor = 0;	/* black text */
		  ue->fontsize = 0;	/* default fontsize */
		  ue->fontname = NULL;	/* default font */
		  uedge_append_edgelist (ue);
		  usubg_add_edge (ue, subg);
		}
	      else
		{
		  /* edge without edge label */
		  ue = uedge_new (fnode, tnode);
		  if (ue == NULL)
		    {
		      return (0);
		    }
		  ue->color = color;
		  ue->style = style;
		  ue->rootedon = subg;
		  uedge_append_edgelist (ue);
		  usubg_add_edge (ue, subg);
		}
	    }
	  /* end of edge */
	  return (1);
	}
      else
	{
	  /* node */
	  if (token == '[')
	    {
	      for (;;)
		{
		  token = dotlex ();
		  if (token == 0)
		    {
		      return (0);
		    }
		  else if (token == ']')
		    {
		      break;
		    }
		  else if (token == DOT_SHAPE)
		    {
		      /* = */
		      token = dotlex ();
		      /* shape "shape" */
		      token = dotlex ();
		    }
		  else if (token == DOT_STYLE)
		    {
		      /* = */
		      token = dotlex ();
		      /* style "style" */
		      token = dotlex ();
		    }
		  else if (token == DOT_FILLCOLOR)
		    {
		      /* = */
		      token = dotlex ();
		      /* colorname "colorname" */
		      token = dotlex ();
		      if (token == DOT_WHITE)
			{
			  color = 0x00ffffff;
			}
		      else if (token == DOT_LIGHTBLUE)
			{
			  color = colorcode ("light blue");
			}
		      else if (token == DOT_LIGHTGREY)
			{
			  color = colorcode ("light grey");
			}
		      else if (token == DOT_LIGHTPINK)
			{
			  color = colorcode ("light pink");
			}
		      else
			{
			  color = 0x00ffffff;
			}
		    }
		  else if (token == DOT_LABEL)
		    {
		      /* = */
		      token = dotlex ();
		      /* "string" */
		      token = dotlex ();
		      flabel = laststring;
		      if (option_nodenames)
			{
			  flabel = NULL;
			}
		    }
		  else
		    {
		      /* = */
		      token = dotlex ();
		      /* ? */
		      token = dotlex ();
		    }
		}
	    }
	  else
	    {
	      token1 = token;
	    }
	  un = uniqnode (fname);
	  if (un == (struct unode *) 0)
	    {
	      un = unode_new (fname);
	      if (flabel)
		{
		  un->label = flabel;
		}
	      else
		{
		  un->label = fname;
		}
	      un->rootedon = subg;
	      /* node is defined by node statement */
	      un->bitflags.defbynode = 1;
	      uniqnode_add (un->name, un);
	      unode_append_nodelist (un);
	      usubg_add_node (un, subg);
	    }
	  else
	    {
	      /* node is created in edge() but defined in this other subgraph */
	      if (un->rootedon == subg)
		{
		  /* oke */
		}
	      else
		{
		  un->rootedon = subg;
		  usubg_add_node (un, subg);
		}
	    }
	  if (flabel)
	    {
	      un->label = flabel;
	    }
	  un->color = color;
	  return (1);
	}
    }
  /* */
  return (0);
}


/* End */
